package com.ruoyi.handler;


import com.google.gson.Gson;
import com.iflytek.mt_scylla.mt_scylla;
import com.ruoyi.config.IflytekAsrConfig;
import com.ruoyi.exception.AsrException;
import com.ruoyi.model.AsrParams;
import com.ruoyi.model.AsrVO;
import com.ruoyi.model.FileAttributeDTO;
import com.ruoyi.model.IflytekAsrVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 科大讯飞语音识别处理
 */
public class IflytekAsrHandler implements InitializingBean, DisposableBean {

    private static final Logger LOGGER = LoggerFactory.getLogger(IflytekAsrHandler.class);

    @Autowired
    private IflytekAsrConfig kdxfAsrConfig;

    private mt_scylla mt;

    // sdk加载状态
    private Boolean loadFlag = false;

    public AsrVO start(AsrParams asrParams){
        try {
            if (!loadFlag){
                String err = "科大讯飞语音初始化失败，请检查环境！";
                LOGGER.error(err);
                throw new AsrException("1",err);
            }
            long startTime = System.currentTimeMillis();
            LOGGER.info("科大讯飞语音识别开始：{}", startTime);
            List<FileAttributeDTO> fileAttributeDTOS = readFile(asrParams);
            AsrVO asrVO = new AsrVO();
            fileAttributeDTOS.forEach(item->{
                String text = this.localIat(item.getBuffers());
                asrVO.setText(asrVO.getText() + text);
            });
            long endTime = System.currentTimeMillis();
            LOGGER.info("科大讯飞语音识别结束：{}，用时：{}", endTime, endTime - startTime);
            asrVO.setCode("0");
            return asrVO;
        }catch (AsrException e){
            return AsrVO.result(e.getCode(), e.getMessage(), "");
        }
    }

    public void firstHandle() {
        LOGGER.info("语音skd初始化开始");
        try{
            this.mt = new mt_scylla();
            this.loadFlag = true;
        }catch (Throwable t){
            String err = "初始化失败，请检查环境！";
            LOGGER.error(err, t);
            // throw new AsrException("1",err,e);
        }
        if (this.loadFlag){
            // 初始化语音识别引擎
            int initret = mt.SCYMTInitializeEx(null);
            if (initret != 0) {
                String err = "请检查IP地址是否正确、网络是否正常开启,错误码是" + initret;
                LOGGER.error(err);
                // throw new AsrException(err);
            }
        }

    }

    public void lastHandle() {
        if (!loadFlag){
            return;
        }
        // 科大讯飞sdk关闭处理
        int uniret = mt.SCYMTUninitializeEx(null);
        if (uniret != 0) {
            String error = "语音skd关闭失败,错误码是" + uniret;
            LOGGER.error(error);
        }else{
            LOGGER.info("语音skd关闭成功");
        }
    }


    /**
     * 根据路径查询语音文件
     * @param asrParams
     * @return
     * @throws IOException
     */
    public List<FileAttributeDTO> readFile(AsrParams asrParams) {
        List<FileAttributeDTO> fileAttributeDTOS= new ArrayList<>();
        if (Objects.nonNull(asrParams.getFileStream())){
            try {
                fileAttributeDTOS.add(handleStream(asrParams.getFileStream()));
            } catch (IOException e) {
                LOGGER.error("文件处理异常", e);
                throw new AsrException("8","文件处理异常",e);
            }
        }
        String path = asrParams.getFilePath();
        if (!StringUtils.isEmpty(path)){
            File file = new File(path);
            if (!file.exists()) {
                LOGGER.error("文件未找到{}", path);
                throw new AsrException("文件未找到" + path);
            }

            if (file.isDirectory()) {
                // 获取路径下的所有文件
                File[] files = file.listFiles();
                for (int i = 0; i < files.length; i++) {
                    fileAttributeDTOS.add(handlerFile(files[i]));
                }
            } else {
                fileAttributeDTOS.add(handlerFile(file));
            }
        }

        return fileAttributeDTOS;
    }


    private FileAttributeDTO handlerFile(File file){
        FileAttributeDTO fileAttributeDTO;
        try(InputStream stream = new FileInputStream(file)) {
            fileAttributeDTO = handleStream(stream);
        } catch (FileNotFoundException e) {
            LOGGER.error("文件未找到", e);
            throw new AsrException("3","文件未找到",e);
        } catch (IOException e) {
            LOGGER.error("文件处理异常", e);
            throw new AsrException("8","文件处理异常",e);
        }
        return fileAttributeDTO;

    }

    private FileAttributeDTO handleStream(InputStream stream) throws IOException {
        FileAttributeDTO fileAttributeDTO = new FileAttributeDTO();
        List<byte[]> buffers = new ArrayList<>();
        int length;
        while (true){
            byte[] bts;
            if (kdxfAsrConfig.getBOnline()) {
                bts = new byte[2560]; // 16k16bit的音频，需要sleep 80ms
            } else {
                bts = new byte[64000]; // 发包率需要小于总音频大小
            }
            length = stream.read(bts);
            if (length == -1) {
                break;
            }
            if (length != bts.length) { // 最后一段的length < bts.length
                byte[] tmp = new byte[length];
                System.arraycopy(bts, 0, tmp, 0, length);
                buffers.add(tmp);
            } else {
                buffers.add(bts);
            }
        }
        if (buffers.size() == 1) {
            byte[] bts = new byte[1];
            buffers.add(bts);
        }
        fileAttributeDTO.setBuffers(buffers);
        return fileAttributeDTO;
    }

    public String iat(byte[] wave, int index){
        Gson gson = new Gson();
        IflytekAsrVO product = null;
        int[] errorCode = new int[1];
        String allresult = "";
        // 第一步 SessionBegin
        String sid = mt.SCYMTSessionBeginEx(kdxfAsrConfig.getSsbparam(), errorCode, null);
        LOGGER.info("session_id : {}" , sid);

        if (errorCode[0] != 0) {
            String err = "请检查IP地址是否正确、网络是否正常开启,错误码是" + errorCode[0];
            LOGGER.error(err);
            throw new AsrException("4",err);
        }

        int len = wave.length;
        // 音频缓存区位置信息
        int audioStatus;
        if (index == 0) { // 第一段音频
            audioStatus = 1;
        } else if (index == 1) { // 最后一段音频
            audioStatus = 4;
        } else { // 中间音频
            audioStatus = 2;
        }
        int[] epStatus = new int[1];
        int[] recogStatus = new int[1];
        int[] ret = new int[1];
        String curRes = "";
        // 第二步 AudioWrite写入音频
        curRes = mt.SCYMTAudioWriteEx(sid, wave, len, audioStatus, epStatus, recogStatus, ret,
                null);
        if (ret[0] != 0) {
            String err = "上传音频出错，错误码为：" + ret[0];
            LOGGER.error(err);
            throw new AsrException("6",err);
        }
        // 判断引擎返回pgs是否为1，为1表示有识别结果可获取
        // 这里是在线的解析，离线只能在SCYMTGetResultEx里获取结果
        if (!curRes.isEmpty()) {
            product = gson.fromJson(curRes, IflytekAsrVO.class);
            // 输出
            if (product.getPgs() == 1) {
                allresult = allresult + product.getResult(); // 获取识别结果
                LOGGER.info(curRes);
            }
        }
        if (recogStatus[0] == 5) {
            LOGGER.info("识别结束");
        }
        return product.getResult();
    }


    public String localIat(List<byte[]> buffers){
        Gson gson = new Gson();
        IflytekAsrVO product;
        int[] errorCode = new int[1];
        String allresult = "";
        // 第一步 SessionBegin
        String sid = mt.SCYMTSessionBeginEx(kdxfAsrConfig.getSsbparam(), errorCode, null);
        LOGGER.info("session_id : {}" , sid);

        if (errorCode[0] != 0) {
            String err = "请检查IP地址是否正确、网络是否正常开启,错误码是" + errorCode[0];
            LOGGER.error(err);
            throw new AsrException("4",err);
        }
        try {
            for (int i = 0; i < buffers.size(); ++i) {
                byte[] wave = buffers.get(i);
                int len = wave.length;
                // 音频缓存区位置信息
                int audioStatus;
                if (i == 0) { // 第一段音频
                    audioStatus = 1;
                } else if (i == buffers.size() - 1) { // 最后一段音频
                    audioStatus = 4;
                } else { // 中间音频
                    audioStatus = 2;
                }
                int[] epStatus = new int[1];
                int[] recogStatus = new int[1];
                int[] ret = new int[1];
                String curRes = "";
                // 第二步 AudioWrite写入音频
                curRes = mt.SCYMTAudioWriteEx(sid, wave, len, audioStatus, epStatus, recogStatus, ret,
                        null);
                if (ret[0] != 0) {
                    String err = "上传音频出错，错误码为：" + ret[0];
                    LOGGER.error(err);
                    throw new AsrException("6",err);
                }
                // 判断引擎返回pgs是否为1，为1表示有识别结果可获取
                // 这里是在线的解析，离线只能在SCYMTGetResultEx里获取结果
                if (!curRes.isEmpty()) {
                    product = gson.fromJson(curRes, IflytekAsrVO.class);
                    // 输出
                    if (product.getPgs() == 1) {
                        allresult = allresult + product.getResult(); // 获取识别结果
                        LOGGER.info(curRes);
                    }
                }
                if (recogStatus[0] == 5) {
                    break; // 识别结束
                }

                if (i == buffers.size() - 1) {
                    long start = System.currentTimeMillis();
                    int[] errCode = new int[1];
                    int timeout = 10;
                    BufferedWriter writer = null;
                    while (recogStatus[0] != 5 && 0 == errCode[0]) {
                        // 第三步 GetResult获取结果
                        String curResult = mt.SCYMTGetResultEx(sid, recogStatus, timeout, errCode, null);
                        if (curResult != null && curResult.length() != 0) {

                            product = gson.fromJson(curResult, IflytekAsrVO.class);
                            // 输出
                            if (product.getPgs() == 1) {
                                allresult = allresult + product.getResult(); // 获取识别结果
                                LOGGER.info(curResult);
                            }

                        }
                    }
                    long end = System.currentTimeMillis();
                }

                try {
                    Thread.sleep(80);
                } catch (InterruptedException e) {
                    LOGGER.error("休眠异常", e);
                    throw new AsrException("7","休眠异常",e);
                }

            }
        } finally {
            // 第四步 结束一路会话
            int endret = mt.SCYMTSessionEndEx(sid);
            if (endret != 0) {
                String error = "会话关闭失败,错误码是" + endret;
                LOGGER.error(error);
                // throw new AsrException("6","会话关闭失败,错误码是" + endret);
            }
        }
        return allresult;
    }




    @Override
    public void afterPropertiesSet() throws Exception {
        this.firstHandle();
    }

    @Override
    public void destroy() throws Exception {
        this.lastHandle();
    }


}
